﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;

using TakePhotoTogether.Communication;
using TakePhotoTogether.Communication.Protocols;
using TakePhotoTogetherTest.Mock;

namespace TakePhotoTogetherTest
{
    [TestClass]
    public class SocketManagerTest : IRemoteDataListener
    {
        private float[] sentBytesCounter = new float[DataPacketFactory.MAX_PRIORITY_LEVEL];
        private float[] receivedBytesCounter = new float[DataPacketFactory.MAX_PRIORITY_LEVEL];

        Random rnd = new Random();
        MockSocketManager mockSocketManager = new MockSocketManager();

        int min_slicing_number = 2;
        int max_slicing_number = 40;

        public SocketManagerTest()
        {
            mockSocketManager.AddDataListener(this, ((int)MessageTypes.SyncPacket));
            mockSocketManager.AddDataListener(new SlicedPacketAssembly(mockSocketManager), (int)MessageTypes.SlicedPacket);
        }

        [TestMethod]
        public void Test_SyncPackets()
        {
            sentBytesCounter[((int)MessageTypes.SyncPacket) - 1] = 0;
            receivedBytesCounter[((int)MessageTypes.SyncPacket) - 1] = 0;

            int number_experiments = 1000;
            int min_slicing_number = 2;
            int max_slicing_number = 40;

            for (int e = 0; e < number_experiments; e++)
            {
                int slicing_number = rnd.Next(min_slicing_number, max_slicing_number);

                DataPacket packet = GenerateRandomSyncPacket();
                sentBytesCounter[((int)MessageTypes.SyncPacket) - 1] += packet.Payload.ToBytes().Length;
                DataPacket[] slicedPackets = packet.SlicingPackets(slicing_number);
                Assert.AreEqual(slicing_number, slicedPackets.Length, "Wrong number of slice received");
                mockSocketManager.SendData(slicedPackets);
            }

            //Assert.AreEqual(sentBytesCounter[((int)MessageTypes.SyncPacket) - 1], receivedBytesCounter[((int)MessageTypes.SyncPacket) - 1], "Wrong number of bytes received");
        }

        [TestMethod]
        public void Test_StreamPackets()
        {
            sentBytesCounter[((int)MessageTypes.StreamPacket) - 1] = 0;
            receivedBytesCounter[((int)MessageTypes.StreamPacket) - 1] = 0;

            int number_experiments = 1000;
            int min_slicing_number = 2;
            int max_slicing_number = 40;

            for (int e = 0; e < number_experiments; e++)
            {
                int slicing_number = rnd.Next(min_slicing_number, max_slicing_number);

                DataPacket packet = GenerateRandomStreamPacket();
                sentBytesCounter[((int)MessageTypes.SyncPacket) - 1] += packet.Payload.ToBytes().Length;
                DataPacket[] slicedPackets = packet.SlicingPackets(slicing_number);
                Assert.AreEqual(slicing_number, slicedPackets.Length, "Wrong number of slice received");
                mockSocketManager.SendData(slicedPackets);
            }
        }

        [TestMethod]
        public void Test_AllPackets()
        {
            for (int i = 0; i < DataPacketFactory.MAX_PRIORITY_LEVEL; i++)
            {
                sentBytesCounter[i] = 0;
                receivedBytesCounter[i] = 0;
            }

            int number_experiments = 10000;
            

            for (int e = 0; e < number_experiments; e++)
            {
                DataPacket streamPacket = GenerateRandomStreamPacket();
                SendPacket(streamPacket);

                DataPacket syncPacket = GenerateRandomSyncPacket();
                SendPacket(syncPacket);
            }
        }

        private void SendPacket(DataPacket packet)
        {
            int slicing_number = rnd.Next(min_slicing_number, max_slicing_number);

            sentBytesCounter[packet.MessageType - 1] += packet.Payload.ToBytes().Length;
            DataPacket[] slicedPackets = packet.SlicingPackets(slicing_number);
            Assert.AreEqual(slicing_number, slicedPackets.Length, "Wrong number of slice received");
            mockSocketManager.SendData(slicedPackets);
        }

        public DataPacket GenerateRandomSyncPacket()
        {
            int imageBytesSize = rnd.Next(1000, 8000);
            byte[] imageBytes = new byte[imageBytesSize];
            int weight = rnd.Next(10, 100);
            int height = rnd.Next(10, 100);

            return DataPacketFactory.GetSyncPacket(imageBytes, weight, height, "image");
        }

        public DataPacket GenerateRandomStreamPacket()
        {
            int imageBytesSize = rnd.Next(1000, 8000);
            byte[] imageBytes = new byte[imageBytesSize];
            int weight = rnd.Next(10, 100);
            int height = rnd.Next(10, 100);

            return DataPacketFactory.GetStreamPacket(imageBytes, weight, height);
        }

        public void ReceiveData(PayloadData data)
        {
            if (data is SyncData)
            {
                receivedBytesCounter[((int)MessageTypes.SyncPacket) - 1] += data.ToBytes().Length;
            }

            if (mockSocketManager.GetNumberOfPacketsOnQueue() == 0)
            {
                for (int i = 0; i < DataPacketFactory.MAX_PRIORITY_LEVEL; i++)
                {
                    Assert.AreEqual(sentBytesCounter[i], receivedBytesCounter[i], "Wrong number of bytes received for packet type " +
                        ((MessageTypes)(i+1)));
                }
            }
        }
    }
}
